/*********************
* Author:Dzlua
* QQ:505544956
* time:2016/08/01
*/
#include "Texture.h"
#include "../edui_sdl.h"

EDUI_NAMESPACE_BEGIN

#ifdef EDUI_TEST_NEW_DELETE
int Texture::m_newdeleteNum = 0;
int Texture::_GetNewDeleteNum()
{
    return m_newdeleteNum;
}
#endif

Texture::Texture() : m_frames(0), m_num(0)
{}

Texture::~Texture()
{
    this->Destroy();
}

Texture* Texture::GetInterface()
{
#ifdef EDUI_TEST_NEW_DELETE
    Texture* texture = new Texture;
    ++m_newdeleteNum;
    Log("[Texture] new texture : %d", texture);
    return texture;
#endif
    return new Texture;
}

void Texture::Release()
{
#ifdef EDUI_TEST_NEW_DELETE
    --m_newdeleteNum;
    Log("[Texture] delete texture : %d", this);
#endif
    delete this;
}

Texture* Texture::ToTexture(ITexture* texture)
{
    return dynamic_cast<Texture*>(texture);
}

void Texture::_Add(SDL_Texture* texture)
{
    this->Destroy();
    m_frames = new TextureFrame[1];
    m_frames[0].texture = texture;
    m_frames[0].x = 0;
    m_frames[0].y = 0;
    m_frames[0].delay = 0;
    m_num = 1;
}

bool Texture::_CeateAndAdd(IRender* render, ISurface* surface)
{
    if (!render || !surface) return false;
    this->Destroy();

    m_num = surface->GetNum();
    m_frames = new TextureFrame[m_num];
    for (int i = 0; i < m_num; ++i){
        m_frames[i].texture = SDL_CreateTextureFromSurface(Render::ToRender(render)->GetSDLRender(), Surface::ToSurface(surface)->GetSDLSurface(i));
        if (!m_frames[i].texture){
            this->Destroy();
            return false;
        }
        Point pt = surface->GetOffset(i);
        m_frames[i].x = pt.x;
        m_frames[i].y = pt.y;
        m_frames[i].delay = surface->GetDelay(i);
    }
    return true;
}

bool Texture::Create(IRender* render, int w, int h, TextureAccess access, const char* name, int id)
{
    if (this->IsOK()) return false;
    if (!render || !name) return false;
    IWindow* wnd = render->GetWindow();
    if (!wnd || !wnd->IsOK()) return false;
    TextureMgr* mgr = wnd->GetTextureMgr();

    SDL_Texture *texture = SDL_CreateTexture(Render::ToRender(render)->GetSDLRender(), SDL_PIXELFORMAT_RGBA8888, access, w, h);
    if (!texture) return false;
    this->_Add(texture);
    
    if (!mgr->Add(name, this, id)){
        this->Destroy();
        return false;
    }
    return true;
}

bool Texture::Create(IRender* render, const char* file, const char* name, int id)
{
    if (this->IsOK()) return false;
    if (!render || !file) return false;
    IWindow* wnd = render->GetWindow();
    if (!wnd || !wnd->IsOK()) return false;
    TextureMgr* mgr = wnd->GetTextureMgr();

    SDL_RWops *rwop = SDL_RWFromFile(file, "rb");
     if (IMG_isGIF(rwop)){
        int num = AG_LoadGIF_RW(rwop, 0, 0);
        AG_Frame *frames = new AG_Frame[num];
        if (AG_LoadGIF_RW(rwop, frames, num) == 0){
            SDL_RWclose(rwop);
            delete frames;
            frames = 0;
            return false;
        }

        m_num = num;
        m_frames = new TextureFrame[m_num];
        for (int i = 0; i < m_num; ++i){
            m_frames[i].texture = SDL_CreateTextureFromSurface(Render::ToRender(render)->GetSDLRender(), frames[i].surface);
            if (!m_frames[i].texture){
                SDL_RWclose(rwop);
                AG_FreeSurfaces(frames, num);
                delete frames;
                frames = 0;
                this->Destroy();
                return false;
            }
            m_frames[i].x = frames[i].x;
            m_frames[i].y = frames[i].y;
            m_frames[i].delay = frames[i].delay;
        }
        AG_FreeSurfaces(frames, num);
        delete frames;
        frames = 0;
    } else {
        SDL_Texture *texture = IMG_LoadTexture(Render::ToRender(render)->GetSDLRender(), file);
        if (!texture){
            SDL_RWclose(rwop);
            return false;
        }
        this->_Add(texture);
    }
    SDL_RWclose(rwop);
    
    if (!name){
        if (!mgr->Add(file, this, id)){
            this->Destroy();
            return false;
        }
    } else {
        if (!mgr->Add(name, this, id)){
            this->Destroy();
            return false;
        }
    }
    return true;
}

bool Texture::Create(IRender* render, ISurface* surface, const char* name, int id)
{
    if (this->IsOK()) return false;
    if (!render || !surface || !surface->IsOK()) return false;

    IWindow* wnd = render->GetWindow();
    if (!wnd || !wnd->IsOK()) return false;
    TextureMgr* mgr = wnd->GetTextureMgr();

    m_num = surface->GetNum();
    m_frames = new TextureFrame[m_num];
    for (int i = 0; i < m_num; ++i){
        m_frames[i].texture = SDL_CreateTextureFromSurface(Render::ToRender(render)->GetSDLRender(), Surface::ToSurface(surface)->GetSDLSurface(i));
        if (!m_frames[i].texture){
            this->Destroy();
            return false;
        }
        Point pt = surface->GetOffset(i);
        m_frames[i].x = pt.x;
        m_frames[i].y = pt.y;
        m_frames[i].delay = surface->GetDelay(i);
    }
    
    if (!name){
        SurfaceMgr* surmgr = IEnginePtr->GetSurfaceMgr();
        name = surmgr->GetName(surface);
        if (!name) return false;
    }
    if (!mgr->Add(name, this, id)){
        this->Destroy();
        return false;
    }
    return true;
}

// create use this function, you must Destroy and Release this texture.
bool Texture::Create(SDL_Texture* texture)
{
    if (this->IsOK()) return false;
    if (!texture) return false;
    this->_Add(texture);
    return true;
}

bool Texture::IsOK()
{
    if (!m_frames) return false;
    return true;
}

void Texture::Destroy()
{
    if (m_frames){
        for (int i = 0; i < m_num; ++i){
            if (m_frames[i].texture){
                SDL_DestroyTexture(m_frames[i].texture);
                m_frames[i].texture = 0;
            }
        }
        delete m_frames;
        m_frames = 0;
        m_num = 0;
    }
}

//Texture number.
int Texture::GetNum()
{
    return m_num;
}

Point Texture::GetOffset(int id)
{
    Point pt;
    if (id >= 0 && id < m_num){
        pt.Reset(m_frames[id].x, m_frames[id].y);
    }
    return pt;
}

int Texture::GetDelay(int id)
{
    if (id < 0 || id >= m_num) return 0;
    return m_frames[id].delay;
}

Size Texture::GetSize(int id)
{
    Size sz;
    if (id >= 0 && id < m_num){
        SDL_QueryTexture(m_frames[id].texture, 0, 0, &sz.w, &sz.h);
    }
    return sz;
}

bool Texture::SetColorMod(Uint8 r, Uint8 g, Uint8 b, int id)
{
    if (id < 0 || id >= m_num) return false;
    return !SDL_SetTextureColorMod(m_frames[id].texture, r, g, b);
}

Color Texture::GetColorMod(int id)
{
    Color col;
    if (id >= 0 && id < m_num){
        SDL_GetTextureColorMod(m_frames[id].texture, &col.r, &col.g, &col.b);
    }
    return col;
}

bool Texture::SetAlphaMod(Uint8 alpha, int id)
{
    if (id < 0 || id >= m_num) return false;
    return !SDL_SetTextureAlphaMod(m_frames[id].texture, alpha);
}

Uint8 Texture::GetAlphaMod(int id)
{
    Uint8 alpha;
    if (id >= 0 && id < m_num){
        SDL_GetTextureAlphaMod(m_frames[id].texture, &alpha);
    }
    return alpha;
}

bool Texture::SetBlendMode(BlendMode mode, int id)
{
    if (id < 0 || id >= m_num) return false;
    return !SDL_SetTextureBlendMode(m_frames[id].texture, (SDL_BlendMode)mode);
}

BlendMode Texture::GetBlendMode(int id)
{
    SDL_BlendMode mode = SDL_BLENDMODE_NONE;
    if (id >= 0 && id < m_num){
        SDL_GetTextureBlendMode(m_frames[id].texture, &mode);
    } else {
        Log("[EDUI] Texture::GetBlendMode : error texture id.");
    }
    return (BlendMode)mode;
}

void Texture::Update(const Rect* rect, const void* pixels, int pitch, int id)
{
    if (id < 0 || id >= m_num) return;
    if (rect){
        SDL_Rect rc = { rect->x, rect->y, rect->w, rect->h };
        SDL_UpdateTexture(m_frames[id].texture, &rc, pixels, pitch);
    } else {
        SDL_UpdateTexture(m_frames[id].texture, 0, pixels, pitch);
    }
}

void Texture::UpdateYUV(const Rect* rect,
                    const Uint8 *Yplane, int Ypitch,
                    const Uint8 *Uplane, int Upitch,
                    const Uint8 *Vplane, int Vpitch, int id)
{
    if (id < 0 || id >= m_num) return;
    if (rect){
        SDL_Rect rc = { rect->x, rect->y, rect->w, rect->h };
        SDL_UpdateYUVTexture(m_frames[id].texture, &rc, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch);
    } else {
        SDL_UpdateYUVTexture(m_frames[id].texture, 0, Yplane, Ypitch, Uplane, Upitch, Vplane, Vpitch);
    }
}

bool Texture::Lock(const Rect* rect, void** pixels, int* pitch, int id)
{
    if (id < 0 || id >= m_num) return false;
    if (rect){
        SDL_Rect rc = { rect->x, rect->y, rect->w, rect->h };
        return !SDL_LockTexture(m_frames[id].texture, &rc, pixels, pitch);
    } else {
        return !SDL_LockTexture(m_frames[id].texture, 0, pixels, pitch);
    }
}

void Texture::Unlock(int id)
{
    if (id < 0 || id >= m_num) return;
    SDL_UnlockTexture(m_frames[id].texture);
}

SDL_Texture* Texture::GetSDLTexture(int id) const
{
    if (id < 0 || id >= m_num) return 0;
    return m_frames[id].texture;
}

EDUI_NAMESPACE_END
